#include "finsh_config.h"


#ifdef FINSH_ENABLE

#include <string.h>
#include "msh.h"
#include "finsh.h"


typedef int (*cmd_function_t)(int argc, char **argv);


#ifdef FINSH_MSH_ENABLE

struct finsh_syscall *_syscall_table_begin  = NULL;
struct finsh_syscall *_syscall_table_end    = NULL;
struct finsh_sysvar *_sysvar_table_begin    = NULL;
struct finsh_sysvar *_sysvar_table_end      = NULL;

#ifndef FINSH_USING_MSH_ONLY
uint8_t msh_is_used(void)
{
    return 1;
}
#else
uint8_t msh_is_used(void)
{
    return 0;
}
#endif

int msh_help(int argc, char **argv)
{
	struct finsh_syscall *index;
	
	finsh_printf("shell commands:\n");
	
	for (index = _syscall_table_begin; index < _syscall_table_end; FINSH_NEXT_SYSCALL(index))
	{
		if (strncmp(index->name, "__cmd_", 6) != 0) continue;
	#if defined(FINSH_DESCRIPTION_ENABLE) && defined(FINSH_SYMTAB_ENABLE)
		finsh_printf("%-16s - %s\n", &index->name[6], index->desc);
	#else
		finsh_printf("%s ", &index->name[6]);
	#endif 
	}
	
	finsh_printf("\n");
	
	return 0;
}
FINSH_FUNCTION_EXPORT_ALIAS(msh_help, __cmd_help, shell help.);

static int msh_split(char *cmd, uint32_t length, char *argv[FINSH_ARG_MAX])
{
	char *ptr;
	uint32_t position;
	uint32_t argc;
	uint32_t i;
	
	ptr = cmd;
	position = 0;
	argc = 0;
	
	while (position < length)
	{
		/* strip bank and tab */
		while ((*ptr==' ' || *ptr=='\t') && position<length)
		{
			*ptr = '\0';
			ptr++;
			position++;
		}
		
		if (argc >= FINSH_ARG_MAX)
		{
			finsh_printf("Too many args! We only Use:\n");
			for (i=0; i<argc; i++)
			{
				finsh_printf("%s ", argv[i]);
			}
			finsh_printf("\n");
		}
		
		if (position >= length) break;
		
		/* handle string */
		if (*ptr == '"')
		{
			ptr++;
			position++;
			
			/* skip this string */
			while (*ptr != '"' && position < length)
			{
				if (*ptr == '\\')
				{
					if ((*ptr + 1) == '"')
					{
						ptr++;
						position++;
					}
				}
				ptr++;
				position++;
			}
			if (position >= length) break;
			
			 /* skip '"' */
			*ptr='\0';
			ptr++;
			position++;
		}
		else
		{
			argv[argc] = ptr;
			argc++;
			while ((*ptr!=' ' && *ptr!='\t') && position<length)
			{
				ptr++;
				position++;
			}
			if (position >= length) break;
		}
	}
	
	return argc;
}

static cmd_function_t msh_get_cmd(char *cmd, int size)
{
	struct finsh_syscall *index;
	cmd_function_t cmd_func = NULL;
	
	for (index=_syscall_table_begin; index<_syscall_table_end; FINSH_NEXT_SYSCALL(index))
	{
		if (strncmp(index->name, "__cmd_", 6) != 0) continue;
		
		if (strncmp(&index->name[6], cmd, size) == 0 &&
			index->name[6 + size] == '\0')
		{
			cmd_func = (cmd_function_t)index->func;
			break;
		}
	}
	
	return cmd_func;
}

static int _msh_exec_cmd(char *cmd, uint32_t length, int *retp)
{
	int argc;
	uint32_t cmd0_size = 0;
	cmd_function_t cmd_func;
	char *argv[FINSH_ARG_MAX];
	
	if (cmd == NULL) return -1;
	if (retp == NULL) return -1;
	
	/* find the size of first command */
	while ((cmd[cmd0_size]!=' ' && cmd[cmd0_size] != '\t') && cmd0_size<length)
	{
		cmd0_size++;
	}
	if (cmd0_size==0)
	{
		return -1;
	}
	
	cmd_func = msh_get_cmd(cmd, cmd0_size);
	if (cmd_func == NULL) return -1;
	
	/* split arguments */
	memset(argv, 0x00, sizeof(argv));
	argc = msh_split(cmd, length, argv);
	if(argc == 0)
	{
		return -1;
	}
	
	/* exec this command */
	*retp = cmd_func(argc, argv);
	
	return 0;
}

/*****/

int msh_exec(char *cmd, uint32_t length)
{
	int cmd_ret;
	
	while (*cmd==' ' || *cmd=='\t')
	{
		cmd++;
		length++;
	}
	
	if (length == 0)
	{
		return 0;
	}
	
	/* Exec sequence:
     * 1. built-in command
     * 2. module(if enabled)
     */
	if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0)
	{
		return cmd_ret;
	}
	
	/* truncate the cmd at the first space. */
	{
		char *tcmd;
		tcmd = cmd;
		while (*tcmd != ' ' && *tcmd != '\0')
		{
			tcmd++;
		}
		*tcmd = '\0';
	}
	
	finsh_printf("%s: command not found.\n", cmd);
	
	return -1;
}

static int str_common(const char *str1, const char *str2)
{
    const char *str = str1;

    while ((*str != 0) && (*str2 != 0) && (*str == *str2))
    {
        str ++;
        str2 ++;
    }

    return (str - str1);
}

void msh_auto_complete(char *prefix)
{
	int length, min_length;
	const char *name_ptr, *cmd_name;
	struct finsh_syscall *index;
	
	min_length = 0;
	name_ptr = NULL;
	
	if (*prefix=='\0')
	{
		msh_help(0, NULL);
		return ;
	}
	
	/* checks in internal command */
	for (index=_syscall_table_begin; index<_syscall_table_end; FINSH_NEXT_SYSCALL(index))
	{
		/* skip finsh shell function */
		if (strncmp(index->name, "__cmd_", 6) != 0) continue;
		
		cmd_name = (const char*)&index->name[6];
		if (strncmp(prefix, cmd_name, strlen(prefix))==0)
		{
			if (min_length == 0)
			{
				name_ptr = cmd_name; /* set name_ptr */
				min_length = strlen(name_ptr); /* set initial length */
			}
			
			length = str_common(name_ptr, cmd_name);
			if (length < min_length)
			{
				min_length = length;
			}
			
			finsh_printf("%s\n",cmd_name);
		}
	}
	
	/* auto complete string */
	if (name_ptr != NULL)
	{
		strncpy(prefix, name_ptr, min_length);
	}
	
}

#endif


#endif
